#include "stdafx.hpp"
#include "ColligereDatabaseError.hpp"
#include "ColligereAudioDatabaseLayout.hpp"
#include "ColligereApp.hpp"
#include "ColligereConfigHandler.hpp"
#include "ColligereIds.hpp"
#include "ColligereCddbDiscId.hpp"
#include "ColligereXmlUtils.hpp"
#include "ColligereAudioDatabaseInterface.hpp"

col::AudioDatabaseLayout::AudioDatabaseLayout 
( wxWindow *pParentWnd, DatabaseInterfaceBase *pParentInterface ) : 
DatabaseLayoutBase ( pParentWnd ), mNextRecBinItemDataPosCounter(0), mpSaveProgDlg(0), 
mpOpenProgDlg(0), mpDatabaseItemsCtrl(0), mpRecycleBinCtrl(0), mpTracksCtrl(0), 
mpNotesCtrl(0), mpBrowseByCtrl(0), mpItemsPanel(0), mpNotebookLayout(0), 
mpTrackLenCtrl(0), mpTrackTitleCtrl(0), mpTrackNumCtrl(0), mpAddTrackButton(0), 
mpDeleteTrackButton(0), mpEditTrackButton(0), mpTagsCtrl(0), mpImportProgDlg(0), 
mpReleaseInfoCtrl(0), mpExportProgDlg(0), mpSearchDiscogsPanel(0), 
mpSearchFreeDbPanel(0), mpParentInterface ( pParentInterface )
{
	wxASSERT ( mpParentInterface );
}

col::AudioDatabaseLayout::~AudioDatabaseLayout()
{

}

void col::AudioDatabaseLayout::Create()
{
	CreateThread ( wxTHREAD_JOINABLE );
    GetThread()->Run();

	Bind ( COLLIGERE_CMD_DATAGETTER_THREAD_COMPLETED,
		&AudioDatabaseLayout::OnThreadCompleted );

	Bind ( wxEVT_CLOSE_WINDOW, &AudioDatabaseLayout::OnClose, GetId() );

	mLayoutMngr.SetManagedWindow ( this );

	CreateLayout();
	ConnectEventHandlers();

	MainFrame *f = wxDynamicCast ( GetParent(), MainFrame );

	f->GetMenuBar()->Check ( ID_Menu_ViewItems, IsDatabaseItemListShown() ); 
	f->GetMenuBar()->Check ( ID_Menu_ViewRecycleBin, IsRecycleBinShown() );
	f->GetMenuBar()->Check ( ID_Menu_SearchFreeDb, IsSearchFreeDbShown() );
	f->GetMenuBar()->Check ( ID_Menu_SearchDiscogs, IsSearchDiscogsShown() );
	f->EnableClearInputs();
}

wxThread::ExitCode col::AudioDatabaseLayout::Entry()
{
	typedef const wxXmlNode node;
	wxXmlDocument doc ( wxGetApp().GetDataDir() + "PreDefinedValues.xml" );
	if ( doc.IsOk() )
	{
		foreach ( node *n, XmlUtils::FindNodes ( doc.GetRoot(), "format" ) )
		{
			mFormatChoices.Add ( XmlUtils::GetNodeContent(n) );
		}
		foreach ( node *n, XmlUtils::FindNodes ( doc.GetRoot(), "condition" ) )
		{
			mConditionChoices.Add ( XmlUtils::GetNodeContent(n) );
		}
		foreach ( node *n, XmlUtils::FindNodes ( doc.GetRoot(), "status" ) )
		{
			mStatusChoices.Add ( XmlUtils::GetNodeContent(n) );
		}
		foreach ( node *n, XmlUtils::FindNodes ( doc.GetRoot(), "rating" ) )
		{
			mRatingChoices.Add ( XmlUtils::GetNodeContent(n) );
		}
		foreach ( node *n, XmlUtils::FindNodes ( doc.GetRoot(), "genre" ) )
		{
			mGenreChoices.Add ( XmlUtils::GetNodeContent(n) );
		}
		foreach ( node *n, XmlUtils::FindNodes ( doc.GetRoot(), "country" ) )
		{
			mCountryChoices.Add ( XmlUtils::GetNodeContent(n) );
		}
	}
	wxQueueEvent ( this, new wxCommandEvent ( COLLIGERE_CMD_DATAGETTER_THREAD_COMPLETED ) );
	return ( wxThread::ExitCode ) 0;
}

void col::AudioDatabaseLayout::Close()
{
	//mLayoutMngr.UnInit();
}

const col::TreeItemData *col::AudioDatabaseLayout::GetSelectedItemData() const
 {
	return mpDatabaseItemsCtrl->GetSelectedItemData();
}

bool col::AudioDatabaseLayout::IsDatabaseItemListShown() const 
{ 
	return mpItemsPanel->IsShown(); 
}

bool col::AudioDatabaseLayout::IsRecycleBinShown() const 
{ 
	return mpRecycleBinCtrl->IsShown(); 
}

bool col::AudioDatabaseLayout::IsSearchDiscogsShown() const
{
	return mpSearchDiscogsPanel->IsShown();
}

bool col::AudioDatabaseLayout::IsSearchFreeDbShown() const
{
	return mpSearchFreeDbPanel->IsShown();
}

void col::AudioDatabaseLayout::ShowDatabaseItems ( bool show ) 
{ 
	mLayoutMngr.GetPane ( mpItemsPanel ).Show ( show );
	mLayoutMngr.Update();
}

void col::AudioDatabaseLayout::ShowRecycleBin ( bool show )
{
	mLayoutMngr.GetPane ( mpRecycleBinCtrl ).Show ( show );
	mLayoutMngr.Update();
}

void col::AudioDatabaseLayout::ShowSearchFreeDb ( bool show )
{
	mLayoutMngr.GetPane ( mpSearchFreeDbPanel ).Show ( show );
	mLayoutMngr.Update();
}

void col::AudioDatabaseLayout::ShowSearchDiscogs ( bool show )
{
	mLayoutMngr.GetPane ( mpSearchDiscogsPanel ).Show ( show );
	mLayoutMngr.Update();
}

void col::AudioDatabaseLayout::ConnectEventHandlers()
{
	mLayoutMngr.Bind ( wxEVT_AUI_PANE_BUTTON, 
		&AudioDatabaseLayout::OnPaneButton );

	Bind ( wxEVT_COMMAND_TEXT_UPDATED, 
		&AudioDatabaseLayout::OnTrackTitleText, mpTrackTitleCtrl->GetId() );
	Bind ( wxEVT_COMMAND_BUTTON_CLICKED,
		&AudioDatabaseLayout::OnButtonAddTrack, mpAddTrackButton->GetId() );
	Bind ( wxEVT_COMMAND_BUTTON_CLICKED,
		&AudioDatabaseLayout::OnButtonDeleteTrack, mpDeleteTrackButton->GetId() );
	Bind ( wxEVT_COMMAND_BUTTON_CLICKED,
		&AudioDatabaseLayout::OnButtonEditTrack, mpEditTrackButton->GetId() );
		
	Bind ( wxEVT_COMMAND_COMBOBOX_SELECTED,
		&AudioDatabaseLayout::OnBrowseBySelected, mpBrowseByCtrl->GetId() );
	
	Bind ( wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK,
		&AudioDatabaseLayout::OnRecycleBinItemRightClick, mpRecycleBinCtrl->GetId() );
	Bind ( wxEVT_COMMAND_LIST_KEY_DOWN,
		&AudioDatabaseLayout::OnRecBinKeyDown, mpRecycleBinCtrl->GetId() );

	Bind ( wxEVT_COMMAND_MENU_SELECTED, 
		&AudioDatabaseLayout::OnPopupMenuRestoreRecBinItem, ID_PopupMenu_RestoreRecBinItem );
	Bind ( wxEVT_COMMAND_MENU_SELECTED, 
		&AudioDatabaseLayout::OnPopupMenuRemRecBinItemPerm, ID_PopupMenu_DeleteRecBinItem );

	Bind ( wxEVT_COMMAND_LIST_ITEM_SELECTED,
		&AudioDatabaseLayout::OnTrackSelected, mpTracksCtrl->GetId() );
	Bind ( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
		&AudioDatabaseLayout::OnTrackDeselected, mpTracksCtrl->GetId() );
	Bind ( wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
		&AudioDatabaseLayout::OnTrackActivated, mpTracksCtrl->GetId() );

	Bind ( wxEVT_COMMAND_TREE_SEL_CHANGED, &AudioDatabaseLayout::OnItemChanged, 
		mpDatabaseItemsCtrl->GetId() );
	Bind ( wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, &AudioDatabaseLayout::OnItemRightClicked,
		mpDatabaseItemsCtrl->GetId() );

    Bind ( wxEVT_PG_CHANGED, &AudioDatabaseLayout::OnPropertyGridChanged, 
		ID_PropertyGrid_ReleaseInfo );

	// Database events

	const wxUint32 db_id = mpParentInterface->GetId();

	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_ADD, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseAddEvent ) ); 
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_CHANGE, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseChangeEvent ) );
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_DELETE, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseDeleteEvent ) );
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_RESTORE, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseRestoreEvent ) );
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_POST, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabasePostEvent ) );
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_QUERIED, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseQueryEvent ) );
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_LIST_COMMIT_START, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseCommitEvent ) ); 
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_LIST_COMMIT_END, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseCommitEvent ) ); 
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_COMMIT_START, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseCommitEvent ) );
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_COMMIT_END, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseCommitEvent ) ); 
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_START, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseQueryEvent ) );
		
	Connect ( db_id, COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_END, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseQueryEvent ) );
	
	Connect ( db_id, COLLIGERE_EVT_DATABASE_USER_REQUEST_CLOSE, 
		ColligereAudioDatabaseEventHandler 
		( &AudioDatabaseLayout::OnDatabaseCloseEvent ) ); 

	// Import / export

	Connect ( COLLIGERE_EVT_IMPORT_START,
		ColligereDataImporterEventHandler ( &AudioDatabaseLayout::OnImport ) );
	
	Connect ( COLLIGERE_EVT_IMPORT_END,
		ColligereDataImporterEventHandler ( &AudioDatabaseLayout::OnImport ) );
	
	Connect ( COLLIGERE_EVT_ITEM_IMPORTED,
		ColligereDataImporterEventHandler ( &AudioDatabaseLayout::OnImport ) );

	Connect ( COLLIGERE_EVT_EXPORT_START,
		ColligereDataExporterEventHandler ( &AudioDatabaseLayout::OnExport ) );
	
	Connect ( COLLIGERE_EVT_EXPORT_END,
		ColligereDataExporterEventHandler ( &AudioDatabaseLayout::OnExport ) );
	
	Connect ( COLLIGERE_EVT_ITEM_EXPORTED,
		ColligereDataExporterEventHandler ( &AudioDatabaseLayout::OnExport ) );
}

void col::AudioDatabaseLayout::CreateLayout()
{
	CreateDatabaseItemList();
	CreateRecycleBin();
	CreateNotebookLayout();
	CreateSearchFreeDbPanel();
	CreateSearchDiscogsPanel();
	LoadLayout();
}

void col::AudioDatabaseLayout::CreateSearchDiscogsPanel()
{
	mpSearchDiscogsPanel = wxDynamicCast 
		( wxXmlResource::Get()->LoadPanel ( this, "ID_Panel_SearchDiscogs" ),
		ColligereSearchDiscogsPanel );

	wxAuiPaneInfo pi;
	pi.Dockable().Right().Dock().Caption( _("Search Discogs") ).Movable().Floatable 
		( true ).Name ( "Search Discogs" );

	mLayoutMngr.AddPane ( mpSearchDiscogsPanel, pi );
}

void col::AudioDatabaseLayout::CreateSearchFreeDbPanel()
{
	mpSearchFreeDbPanel = wxDynamicCast 
		( wxXmlResource::Get()->LoadPanel ( this, "ID_Panel_SearchFreeDb" ),
		ColligereSearchFreeDbPanel );
	mpSearchFreeDbPanel->Initialize();

	wxAuiPaneInfo pi;
	pi.Dockable().Right().Dock().Caption( _("Search FreeDb") ).Movable().Floatable 
		( true ).Name ( "Search FreeDb" );

	mLayoutMngr.AddPane ( mpSearchFreeDbPanel, pi );
}

void col::AudioDatabaseLayout::CreateNotebookLayout()
{
	wxASSERT ( !mpNotebookLayout );

	mpNotebookLayout = new wxAuiNotebook ( this, wxID_ANY, wxDefaultPosition, 
		wxDefaultSize, wxAUI_NB_TOP | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE |
		wxAUI_NB_SCROLL_BUTTONS | wxNO_BORDER );

	CreateReleaseInfoPage();
	CreateNotesPage();
	CreateTracksPage();

	wxAuiPaneInfo pi;
	pi.Dockable().Center().Dock().CaptionVisible 
		( false ).Movable().Floatable ( false ).Name 
		( "Notebook Layout" ).PaneBorder ( false );

	mLayoutMngr.AddPane ( mpNotebookLayout, pi );
}

void col::AudioDatabaseLayout::CreateDatabaseItemList()
{
	mpItemsPanel = wxXmlResource::Get()->LoadPanel ( this, "ID_Panel_LeftSideBar" );

    mpBrowseByCtrl = XRCCTRL ( *mpItemsPanel, "ID_ComboBox_BrowseBy", wxComboBox );
    mpDatabaseItemsCtrl = XRCCTRL ( *mpItemsPanel, "ID_TreeCtrl_DatabaseItems",
		ColligereAudioDatabaseTreeCtrl );

	typedef ColligereAudioDatabaseTreeCtrl::BrowseBy bb;

	mpBrowseByCtrl->Append ( _("Artist"), new BrowseByClientData ( bb::Artist ) );
	mpBrowseByCtrl->Append ( _("Genre"), new BrowseByClientData ( bb::Genre ) );
	mpBrowseByCtrl->Append ( _("Format"), new BrowseByClientData ( bb::Format ) );
	mpBrowseByCtrl->Append ( _("Year"), new BrowseByClientData ( bb::Year ) );
	mpBrowseByCtrl->Append ( _("Country"), new BrowseByClientData ( bb::Country ) );
	mpBrowseByCtrl->Append ( _("Tag"), new BrowseByClientData ( bb::Tag ) );

	mpBrowseByCtrl->SetSelection(0);

	mpDatabaseItemsCtrl->Initialize();

	wxAuiPaneInfo items;
	
	items.Dockable().Left().Dock().CloseButton().CaptionVisible().Movable().
		Resizable().Name ( "Database Items" ).Caption ( _("Database Items") );

	mLayoutMngr.AddPane ( mpItemsPanel, items );
}

void col::AudioDatabaseLayout::CreateRecycleBin()
{
	mpRecycleBinCtrl = new ColligereListCtrl ( this, wxID_ANY, wxDefaultPosition,
		wxSize ( 150, 100 ), wxLC_REPORT | wxLC_SINGLE_SEL );

	const wxString artist_in
		( capitalize ( Artist().GetTableDescriptor().GetItemName() ) );
	const wxString album_in
		( capitalize ( Album().GetTableDescriptor().GetItemName() ) );

	mpRecycleBinCtrl->InsertColumn ( 0, artist_in );
	mpRecycleBinCtrl->InsertColumn ( 1, album_in );

	wxAuiPaneInfo rec_bin;
	rec_bin.Dockable().Left().Dock().CaptionVisible().Movable().Resizable().Name 
		( "Recycle Bin" ).CloseButton().Caption (  _("Recycle Bin") );

	mLayoutMngr.AddPane ( mpRecycleBinCtrl, rec_bin );
}

void col::AudioDatabaseLayout::OnPaneButton ( wxAuiManagerEvent &rEvent )
{
	if ( rEvent.button == wxAUI_BUTTON_CLOSE )
	{
		if ( rEvent.pane->name == "Database Items" )
		{
			wxGetApp().GetFrame()->ProcessCommand ( ID_Menu_ViewItems );
		}
		else if ( rEvent.pane->name == "Recycle Bin" )
		{
			wxGetApp().GetFrame()->ProcessCommand ( ID_Menu_ViewRecycleBin );
		}
		else if ( rEvent.pane->name == "Search FreeDb" )
		{
			wxGetApp().GetFrame()->ProcessCommand ( ID_Menu_SearchFreeDb );
		}
		else if ( rEvent.pane->name == "Search Discogs" )
		{
			wxGetApp().GetFrame()->ProcessCommand ( ID_Menu_SearchDiscogs );
		}
		else
		{
			wxFAIL_MSG ( "What pane?" );
		}
	}
}

void col::AudioDatabaseLayout::CreateReleaseInfoPage()
{
    wxPropertyGrid *pg = mpReleaseInfoCtrl = new wxPropertyGrid ( mpNotebookLayout, 
		ID_PropertyGrid_ReleaseInfo, wxDefaultPosition, wxDefaultSize, 
		wxPG_SPLITTER_AUTO_CENTER | wxPG_BOLD_MODIFIED | wxPGMAN_DEFAULT_STYLE |
		wxNO_BORDER );

	pg->SetExtraStyle ( wxPG_EX_HELP_AS_TOOLTIPS );

	pg->AddActionTrigger ( wxPG_ACTION_NEXT_PROPERTY, WXK_RETURN );

    pg->Append ( new wxPropertyCategory ( _("Release Information"), "Release Information" ) );
	
	pg->Append ( new wxStringProperty ( _("Artist"), "Artist" ) );
	pg->SetPropertyHelpString ( "Artist", _("This field is required.") );
	pg->Append ( new wxStringProperty ( _("Album"), "Album" ) );
	pg->SetPropertyHelpString ( "Album", _("This field is required.") );

	pg->Append ( new wxEditEnumProperty ( _("Format"), "Format" ) );
	pg->Append ( new wxEditEnumProperty ( _("Genre"), "Genre" ) );
	pg->Append ( new wxEditEnumProperty ( _("Country"), "Country" ) );

	pg->Append ( new wxStringProperty ( _("Year"), "Year" ) );

	pg->Append ( new wxStringProperty ( _("Label"), "Label" ) );
	pg->Append ( new wxStringProperty ( _("Catalog #"), "CatalogNum" ) );
	pg->Append ( new wxStringProperty ( _("Homepage"), "Homepage" ) );
	
	pg->Append ( new wxEditEnumProperty ( _("Status"), "Status" ) );

	pg->Append ( new wxPropertyCategory ( _("Library"), "Library" ) );

	pg->Append ( new wxEditEnumProperty ( _("Condition"), "Condition" ) );
	pg->Append ( new wxEditEnumProperty ( _("Rating"), "Rating" ) );
	pg->Append ( new wxStringProperty ( _("Purchase Price"), "Purchase Price" ) );
	pg->Append ( new wxStringProperty ( _("Sell Price"), "Sell Price" ) );

	const wxColour bg_colour1 ( 255, 239, 240 );
	const wxColour bg_colour2 ( 239, 255, 240 );

	for ( wxPropertyGridIterator it = pg->GetIterator(); !it.AtEnd(); ++it )
    {
        wxPGProperty *p = *it;
    
		const wxString name ( p->GetName() );

		if ( name == "Artist" || name == "Album" )
		{
			p->SetCell ( 1, wxPGCell ( name, wxNullBitmap, wxNullColour, bg_colour1 ) );
			continue;
		}
		p->SetCell ( 1, wxPGCell ( name, wxNullBitmap, wxNullColour, bg_colour2 ) );
	}
	mpNotebookLayout->AddPage ( pg, _("Release Information") );
}

void col::AudioDatabaseLayout::CreateNotesPage()
{
	wxPanel *page = wxXmlResource::Get()->LoadPanel ( mpNotebookLayout,
		"ID_NotebookPage_Notes" );

	wxASSERT ( page );

	mpNotesCtrl = XRCCTRL ( *page, "ID_TextCtrl_Notes", wxTextCtrl );
	mpTagsCtrl = XRCCTRL ( *page, "ID_TextCtrl_Tags", wxTextCtrl );

	XRCCTRL ( *mpNotebookLayout, "ID_ScrolledWindow_Notes", 
		wxScrolledWindow )->SetScrollRate ( 10, 10 );

	mpNotebookLayout->AddPage ( page, _("Notes") );
}

void col::AudioDatabaseLayout::CreateTracksPage()
{
	wxPanel *page = wxXmlResource::Get()->LoadPanel ( mpNotebookLayout,
		"ID_NotebookPage_Tracks" );
	wxASSERT ( page );

	mpTracksCtrl = XRCCTRL ( *page, "ID_ListCtrl_Tracks", ColligereListCtrl );
	mpTrackLenCtrl = XRCCTRL ( *page, "ID_TextCtrl_TrackLen", wxTextCtrl );
	mpTrackTitleCtrl = XRCCTRL ( *page, "ID_TextCtrl_TrackTitle", wxTextCtrl );
	mpTrackNumCtrl = XRCCTRL ( *page, "ID_TextCtrl_TrackNum", wxTextCtrl );
	
	mpAddTrackButton = XRCCTRL ( *page, "ID_BitmapButton_AddTrack", wxBitmapButton );
	mpAddTrackButton->SetBitmapLabel ( wxGetApp().GetIcon ( App::IconIndex::EditAdd ) );
	mpDeleteTrackButton = XRCCTRL ( *page, "ID_Button_DeleteTrack", wxButton );
	mpEditTrackButton = XRCCTRL ( *page, "ID_Button_EditTrack", wxButton );

	const wxUint32 ctrl_wd = mpTracksCtrl->GetClientSize().GetWidth();
	const wxUint32 ch_wd = mpTracksCtrl->GetCharWidth();

	mpTracksCtrl->InsertColumn ( 0, "#", wxLIST_FORMAT_CENTER, ch_wd * 4 );
	mpTracksCtrl->InsertColumn ( 2, _("Length"), wxLIST_FORMAT_CENTER, ch_wd * 8 );
	mpTracksCtrl->InsertColumn ( 1, _("Title"), wxLIST_FORMAT_LEFT );

	mpTracksCtrl->SetColumnWidth ( 1, ctrl_wd - ( mpTracksCtrl->GetColumnWidth(0),
		mpTracksCtrl->GetColumnWidth(2) ) );

	mpTrackNumCtrl->SetValidator ( wxTextValidator ( wxFILTER_NUMERIC ) );

	wxTextValidator track_len ( wxFILTER_INCLUDE_CHAR_LIST );
	wxArrayString inc;

	for ( wxUint32 i = 0; i < 10; ++i ) { inc.Add ( wxString() << i ); }

	inc.Add (":");
	track_len.SetIncludes ( inc );
	mpTrackLenCtrl->SetValidator ( track_len );

	// Scrollbars are not visible without SetScrollRate(). 
	// AFAIK, this is a bug in wxWidgets.
	XRCCTRL ( *mpNotebookLayout, "ID_ScrolledWindow_Tracks", 
		wxScrolledWindow )->SetScrollRate ( 10, 10 );

	mpNotebookLayout->AddPage ( page, _("Tracks") );
}

void col::AudioDatabaseLayout::UnselectActiveItem()
{
	mpDatabaseItemsCtrl->Unselect();
	ClearInputControls();
}

void col::AudioDatabaseLayout::LoadLayout()
{
	const wxString layout ( ConfigHandler::Get().GetValue
		( ConfigVariable::AudioDatabaseWxAuiSettings ) );

	if ( !layout.empty() )
	{
		mLayoutMngr.LoadPerspective ( layout, false );
	}
	mLayoutMngr.Update();
}

void col::AudioDatabaseLayout::SaveLayout()
{
	ConfigHandler::Get().SetValue ( ConfigVariable::AudioDatabaseWxAuiSettings, 
		mLayoutMngr.SavePerspective() );
}

const col::Artist &col::AudioDatabaseLayout::GetItemFromInputControls()
{
	mpReleaseInfoCtrl->CommitChangesFromEditor();

	mCurParsed = Artist();

	foreach ( const Column &col, mCurParsed.GetTableDescriptor().GetColumns() )
	{
		const ControlLink &ctrl_link = col.GetCtrlLink();
		switch ( ctrl_link.GetType() )
		{
		case ControlLink::Type::NormalInputField:
			{
				mCurParsed.SetColumnValue ( col.GetIndex(), gui::get_ctrl_value 
					( XRCID ( ctrl_link.GetXrcId().c_str() ), GetId() ) );
				break;
			}
		case ControlLink::Type::PropertyGrid:
			{
				if ( const wxPGProperty *p = mpReleaseInfoCtrl->GetPropertyByName 
					( ctrl_link.GetPropName() ) )
				{
					mCurParsed.SetColumnValue ( col.GetIndex(), p->GetValueAsString() );
				}
				break;
			}
		default:
			break;
		}
	}

	Album album;

	foreach ( const Column &col, album.GetTableDescriptor().GetColumns() )
	{
		const ControlLink &cl = col.GetCtrlLink();
		switch ( cl.GetType() )
		{
		case ControlLink::Type::NormalInputField:
			{
				album.SetColumnValue ( col.GetIndex(), gui::get_ctrl_value 
					( XRCID ( cl.GetXrcId().c_str() ), GetId() ) );
				break;
			}
		case ControlLink::Type::PropertyGrid:
			{
				if ( const wxPGProperty *p = mpReleaseInfoCtrl->GetPropertyByName 
					( cl.GetPropName() ) )
				{
					album.SetColumnValue ( col.GetIndex(), p->GetValueAsString() );
				}
				break;
			}
		default:
			break;
		}
	}
	for ( wxInt32 track = 0; track < mpTracksCtrl->GetItemCount(); ++track )
	{
		album.AddChild ( Track ( mpTracksCtrl->GetItemText
			( track ), mpTracksCtrl->GetColumnText ( track, 1 ), 
			mpTracksCtrl->GetColumnText ( track, 2 ) ) );
	}
	mCurParsed.AddChild ( album );

	return mCurParsed;
}

void col::AudioDatabaseLayout::PassItemToInputControls
( const DatabaseItemBase &rItem )
{
	MainFrame *fr = wxDynamicCast ( GetParent(), MainFrame );
	fr->EnableItemOperations ( false );
	fr->EnableClearInputs();

	if ( const Artist *artist = dynamic_cast < const Artist * > ( &rItem ) )
	{
		DatabaseLayoutBase::PassItemToInputControls ( *artist );
		mPrevParsed = *artist;
	}
	else if ( const Album *album = dynamic_cast < const Album * > ( &rItem ) )
	{
		DatabaseLayoutBase::PassItemToInputControls ( *album );
		mPrevParsed = dynamic_cast < const Artist & > ( album->GetParent() );

		if ( album->GetChildCount() > 0 )
		{
			mpTracksCtrl->DeleteAllItems();

			foreach ( const DatabaseItemBasePtr track, album->GetChildren() )
			{
				DatabaseLayoutBase::PassItemToInputControls ( *track );
			}
		}
	}
	else if ( const Track *track = dynamic_cast 
		< const Track * > ( &rItem ) )
	{
		DatabaseLayoutBase::PassItemToInputControls ( *track );
	}

	if ( mpDatabaseItemsCtrl->GetSelection().IsOk() )
	{
		fr->EnableDeselectItem();

		if ( IsAlbumItem ( mpDatabaseItemsCtrl->GetSelection() ) )
		{
			fr->EnableUpdate();
			fr->EnableCopy();
			fr->EnableDelete();
			fr->EnableAdding ( false );
			return;
		}
		else if ( IsArtistItem ( mpDatabaseItemsCtrl->GetSelection() ) )
		{
			fr->EnableDelete();
		}
	}
	const bool needed_nfo_filled = ( !mpReleaseInfoCtrl->GetPropertyByName 
		( "Artist" )->GetValueAsString().empty() && !mpReleaseInfoCtrl->GetPropertyByName 
		( "Album" )->GetValueAsString().empty() );

	fr->EnableAdding ( needed_nfo_filled );
}

const wxString col::AudioDatabaseLayout::GetStatusText ( wxUint32 statBarColumn ) const
{
	using namespace boost;

	switch ( statBarColumn )
	{
	case 1:
		{
			const wxUint32 album_count = GetAlbumItemCount(),
				artist_count = GetArtistItemCount();

			if ( album_count != 0 )
			{
				if ( artist_count != 0 )
				{
					return str ( format ( _("Artists: %1% | Albums: %2%") ) %
						artist_count % album_count );
				}
				else
				{
					return str ( format ( _("Albums: %1%") ) % album_count );
				}
			}
			return empty_string;
		}
		break;
	case 2:
		return wxFileName ( mpParentInterface->GetHandler()->GetInputFile() ).GetFullPath();
	default:
		break;
	}
	return empty_string;
}

const wxString col::AudioDatabaseLayout::GetTitleText() const 
{
	return boost::str ( boost::format ( "%1% - %2%" ) %  wxGetApp().GetAppName() % 
		wxFileName ( mpParentInterface->GetHandler()->GetInputFile() ).GetFullName() );
}

void col::AudioDatabaseLayout::SelectItem ( const DatabaseItemBase &rItem, bool select )
{
	mpDatabaseItemsCtrl->SelectItem ( rItem, select );
}

wxUint32 col::AudioDatabaseLayout::GetAlbumItemCount ( const wxTreeItemId &rRoot ) const
{
	wxTreeItemId item = rRoot.IsOk() ? rRoot : mpDatabaseItemsCtrl->GetRootItem();

	wxUint32 count = 0;

	boost::scoped_ptr < wxtreectrl_tree_adapter <> > ta 
		( new  wxtreectrl_tree_adapter <> ( mpDatabaseItemsCtrl ) );

	traverse < wxTreeItemId > ( item, *ta, AlbumNodeCounter 
		( mpDatabaseItemsCtrl, count ) );

	return count;
}

wxUint32 col::AudioDatabaseLayout::GetArtistItemCount ( const wxTreeItemId &rRoot ) const
{
	wxTreeItemId item = rRoot.IsOk() ? rRoot : mpDatabaseItemsCtrl->GetRootItem();

	wxUint32 count = 0;

	boost::scoped_ptr < wxtreectrl_tree_adapter <> > ta 
		( new  wxtreectrl_tree_adapter <> ( mpDatabaseItemsCtrl ) );

	traverse < wxTreeItemId > ( item, *ta, ArtistNodeCounter 
		( mpDatabaseItemsCtrl, count ) );

	return count;
}

bool col::AudioDatabaseLayout::IsArtistItem ( const wxTreeItemId &rItem ) const
{
	if ( rItem.IsOk() )
	{
		return dynamic_cast < ArtistTreeItemData * > 
			( mpDatabaseItemsCtrl->GetItemData ( rItem ) ) != 0;
	}
	return 0;
}

bool col::AudioDatabaseLayout::IsAlbumItem ( const wxTreeItemId &rItem ) const
{
	if ( rItem.IsOk() )
	{
		return dynamic_cast < AlbumTreeItemData * > 
			( mpDatabaseItemsCtrl->GetItemData ( rItem ) ) != 0;
	}
	return 0;
}

////////////////////////////////
// Event handlers
////////////////////////////////

void col::AudioDatabaseLayout::OnItemChanged ( wxTreeEvent &rEvent )
{
	ClearInputControls();

	wxTreeItemId item = rEvent.GetItem();

	if ( item.IsOk() && mpDatabaseItemsCtrl->GetRootItem() != item )
	{
		if ( const TreeItemData *data = dynamic_cast < const TreeItemData * >
			( mpDatabaseItemsCtrl->GetItemData ( item ) ) )
		{
			wxUint32 id = mpParentInterface->GetId();

			if ( const AlbumTreeItemData *album_item = dynamic_cast
				< const AlbumTreeItemData * > ( data ) )
			{
				/* wxLogDebug ( "Album selected (AlbumID: %d / ArtistID: %d).", 
					album_item->GetAlbumId(), album_item->GetArtistId() ); */

				DatabaseRequestEvent req ( album_item->GetAlbumId(),
					album_item->GetArtistId(), "album", id );
				mpParentInterface->GetHandler()->ProcessEvent ( req );

			}
			else if ( const ArtistTreeItemData *artist_item = dynamic_cast 
				< const ArtistTreeItemData * > ( data ) )
			{
				//wxLogDebug ( "Artist selected (ID: %d)", artist_item->GetArtistId() );
				DatabaseRequestEvent req ( artist_item->GetArtistId(), 0, "artist", id );
				mpParentInterface->GetHandler()->ProcessEvent ( req );
			}
		}
	}
	else
	{
		MainFrame *fr = wxDynamicCast ( GetParent(), MainFrame );
		fr->EnableItemOperations ( false );
		fr->EnableClearInputs();
	}
}

void col::AudioDatabaseLayout::OnTrackTitleText ( wxCommandEvent & )
{
	mpAddTrackButton->Enable ( !mpTrackTitleCtrl->IsEmpty() );
}

void col::AudioDatabaseLayout::OnButtonEditTrack ( wxCommandEvent & )
{
	wxDialog dlg;

	wxXmlResource::Get()->LoadDialog ( &dlg, this, "ID_Dialog_EditTrack" );

	// Only allow numbers in the track number control.
	XRCCTRL ( dlg, "ID_TextCtrl_TrackNum", wxTextCtrl )->SetValidator 
		( wxTextValidator ( wxFILTER_NUMERIC ) );

	// Track length validator allows numbers from 0-9 and ':' as the separator.

	wxTextValidator track_len ( wxFILTER_INCLUDE_CHAR_LIST );
	wxArrayString inc;

	for ( wxUint32 i = 0; i < 10; ++i ) { inc.Add ( wxString() << i ); }
	inc.Add (":");
	track_len.SetIncludes ( inc );
	XRCCTRL ( dlg, "ID_TextCtrl_TrackLen", wxTextCtrl )->SetValidator ( track_len );

	const wxUint32 sel_item = mpTracksCtrl->GetSelectedItem();
	wxASSERT ( sel_item != -1 );

	XRCCTRL ( dlg, "ID_TextCtrl_TrackNum", wxTextCtrl )->SetValue
		( mpTracksCtrl->GetColumnText ( sel_item, 0 ) );
	XRCCTRL ( dlg, "ID_TextCtrl_TrackTitle", wxTextCtrl )->SetValue
		( mpTracksCtrl->GetColumnText ( sel_item, 1 ) );
	XRCCTRL ( dlg, "ID_TextCtrl_TrackLen", wxTextCtrl )->SetValue
		( mpTracksCtrl->GetColumnText ( sel_item, 2 ) );

	if ( dlg.ShowModal() == wxID_OK )
	{
		mpTracksCtrl->SetColumnText ( sel_item, 0, 
			XRCCTRL ( dlg, "ID_TextCtrl_TrackNum", wxTextCtrl )->GetValue() );
		
		const wxString title ( XRCCTRL ( dlg, "ID_TextCtrl_TrackTitle", 
			wxTextCtrl )->GetValue() );

		if ( !title.empty() )
		{
			mpTracksCtrl->SetColumnText ( sel_item, 1, title );
		}
		mpTracksCtrl->SetColumnText ( sel_item, 2, XRCCTRL ( dlg, 
			"ID_TextCtrl_TrackLen", wxTextCtrl )->GetValue() );
	}
}

void col::AudioDatabaseLayout::OnButtonAddTrack ( wxCommandEvent & )
{
	const wxString track_title ( mpTrackTitleCtrl->GetValue() );

	// The OnTrackTitleText event handler should have disabled
	// the "Add Track" button if the title was empty.
	wxASSERT ( !track_title.empty() );

	wxString track_num ( mpTrackNumCtrl->GetValue() );
	const wxUint32 item_count = mpTracksCtrl->GetItemCount();

	if ( track_num.empty() )
	{
		track_num << item_count + 1;
	}
	const wxUint32 indx ( mpTracksCtrl->InsertItem ( item_count, track_num ) );

	mpTracksCtrl->SetColumnText ( indx, 1, track_title );
	mpTracksCtrl->SetColumnText ( indx, 2, mpTrackLenCtrl->GetValue() );

	mpTrackNumCtrl->Clear();
	mpTrackLenCtrl->Clear();
	mpTrackTitleCtrl->Clear();

	mpTracksCtrl->EnsureVisible ( indx );
}

void col::AudioDatabaseLayout::OnButtonDeleteTrack ( wxCommandEvent & )
{
	mpTracksCtrl->DeleteItem ( mpTracksCtrl->GetSelectedItem() );
}

void col::AudioDatabaseLayout::OnDatabaseAddEvent ( AudioDatabaseEvent &rEvt )
{
	// Don't select the item if we're currently importing.
	const bool select_item = ( mpImportProgDlg ? 
		mpImportProgDlg->IsBeingDeleted() : true );

	mpDatabaseItemsCtrl->AppendItem ( *rEvt.GetAlbum(), select_item );
	mpDatabaseItemsCtrl->SortCollection();
	mpReleaseInfoCtrl->ClearModifiedStatus();
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnDatabasePostEvent ( AudioDatabaseEvent &rEvt )
{
	const TreeItemData *data = mpDatabaseItemsCtrl->GetSelectedItemData();
	wxASSERT ( data );

	ClearInputControls();

	if ( dynamic_cast < const AlbumTreeItemData * > ( data ) )
	{
		PassItemToInputControls ( *rEvt.GetArtist() );
		PassItemToInputControls ( *rEvt.GetAlbum() );
	}
	else if ( dynamic_cast < const ArtistTreeItemData * > ( data ) )
	{
		PassItemToInputControls ( *rEvt.GetArtist() );
	}
	mpReleaseInfoCtrl->ClearModifiedStatus();
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnDatabaseChangeEvent ( AudioDatabaseEvent &rEvt )
{
	mpDatabaseItemsCtrl->UpdateItem ( *rEvt.GetAlbum() );
	mpReleaseInfoCtrl->ClearModifiedStatus();
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnDatabaseCloseEvent ( AudioDatabaseEvent &rEvt )
{
	if ( ConfigHandler::Get().IsEnabled ( ConfigVariable::SaveLayoutOnExit ) )
	{
		SaveLayout();
	}
	mLayoutMngr.UnInit();
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnDatabaseDeleteEvent ( AudioDatabaseEvent &rEvt )
{
	const Artist *artist = rEvt.GetArtist();
	const Album *album = rEvt.GetAlbum();

	if ( album->IsDeleted() )
	{
		if ( album->IsTrashed() )
		{
			mpRecycleBinCtrl->DeleteItem ( mpRecycleBinCtrl->GetSelectedItem() );
		}
		else
		{
			mpDatabaseItemsCtrl->DeleteItem ( AlbumTreeItemData 
				( artist->GetId(), album->GetId(), "" ) );
		}
	}
	else if ( album->IsTrashed() )
	{
		const unsigned long indx = mpRecycleBinCtrl->InsertItem 
			( mpRecycleBinCtrl->GetItemCount(), artist->GetArtist() );
		mpRecycleBinCtrl->SetColumnText ( indx, 1, album->GetAlbum() );
		
		const unsigned long data_pos_counter = ++mNextRecBinItemDataPosCounter;
		mRecycleBinCtrlItemData[data_pos_counter] = album;
		mpRecycleBinCtrl->SetItemData ( indx, data_pos_counter );
		mpDatabaseItemsCtrl->DeleteItem ( AlbumTreeItemData 
			( artist->GetId(), album->GetId(), album->GetYear() ) );
	}
	mpReleaseInfoCtrl->ClearModifiedStatus();
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnDatabaseRestoreEvent ( AudioDatabaseEvent &rEvt )
{
	mpDatabaseItemsCtrl->AppendItem ( *rEvt.GetAlbum() );

	mpRecycleBinCtrl->DeleteItem ( mpRecycleBinCtrl->GetSelectedItem() );

	if ( mpRecycleBinCtrl->GetItemCount() == 0 )
	{
		mRecycleBinCtrlItemData.clear();
	}
	mpDatabaseItemsCtrl->SortCollection();
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnDatabaseCommitEvent ( AudioDatabaseEvent &rEvt )
{
	const wxInt32 type = rEvt.GetEventType();

	static wxUint32 cur_prog_pos = 0, changed_item_count = 0, cur_item = 0;

	if ( type == COLLIGERE_EVT_DATABASE_ITEM_LIST_COMMIT_START )
	{
		cur_item = 0;
		cur_prog_pos = 0;
		try
		{
			const DatabaseHandlerBase &dbh = 
				dynamic_cast < const DatabaseHandlerBase & > ( *rEvt.GetEventObject() );

			changed_item_count = dbh.GetChangedItemCount();

			mpSaveProgDlg = new wxProgressDialog ( _("Saving..."), _("Saving, please wait..."), 
				changed_item_count, this, wxPD_APP_MODAL | wxPD_SMOOTH | 
				wxPD_AUTO_HIDE | wxPD_ELAPSED_TIME );
			mpSaveProgDlg->SetFocus();
		}
		catch ( std::bad_cast &rE )
		{
			wxFAIL_MSG ( rE.what() );
		};
	}
	else if ( type == COLLIGERE_EVT_DATABASE_ITEM_COMMIT_END )
	{
		wxASSERT ( mpSaveProgDlg );
		mpSaveProgDlg->Update ( ++cur_prog_pos );
	}
	else if ( type == COLLIGERE_EVT_DATABASE_ITEM_LIST_COMMIT_END )
	{
		wxASSERT ( mpSaveProgDlg );
		mpSaveProgDlg->Destroy();
		wxLogStatus ( _("Database Saved") );
	}
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnDatabaseQueryEvent ( AudioDatabaseEvent &rEvt )
{
	static wxUint32 cur_prog_pos = 0, album_count = 0;

	if ( rEvt.GetEventType() == COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_START )
	{
		cur_prog_pos = 0;

		const wxUint32 tree_album_count = GetAlbumItemCount();

		if ( tree_album_count != 0 )
		{
			album_count = tree_album_count;
		}
		else
		{
			try
			{
				DatabaseHandlerBase &dbh = 
					dynamic_cast < DatabaseHandlerBase & > ( *rEvt.GetEventObject() );

				album_count = dbh.GetRowCount ( Album().GetTableDescriptor() );
			}
			catch ( std::bad_cast &rE )
			{
				wxFAIL_MSG ( rE.what() );
			}
		}
		mpOpenProgDlg = new wxProgressDialog ( _("Loading..."), 
			_("Loading, please wait..."), album_count, this, 
			wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME );

		mpOpenProgDlg->SetFocus();

		mpDatabaseItemsCtrl->ClearItemList();
		mpRecycleBinCtrl->DeleteAllItems();
	}
	else if ( rEvt.GetEventType() == COLLIGERE_EVT_DATABASE_ITEM_QUERIED )
	{
		wxASSERT ( mpOpenProgDlg );

		const Artist *artist = rEvt.GetArtist();
		const Album *album = rEvt.GetAlbum();

		if ( album->IsTrashed() )
		{
			const wxUint32 indx = mpRecycleBinCtrl->InsertItem 
				( mpRecycleBinCtrl->GetItemCount(), artist->GetArtist() );
			const wxUint32 data_pos_counter = ++mNextRecBinItemDataPosCounter;

			mRecycleBinCtrlItemData[data_pos_counter] = album;
			mpRecycleBinCtrl->SetItemData ( indx, data_pos_counter );
			mpRecycleBinCtrl->SetColumnText ( indx, 1, album->GetAlbum() );
		}
		else
		{
			mpOpenProgDlg->Update ( ++cur_prog_pos );
			mpDatabaseItemsCtrl->AppendItem ( *album );
		}
	}
	else if ( rEvt.GetEventType() == COLLIGERE_EVT_DATABASE_ITEM_LIST_QUERY_ALL_END )
	{
		wxASSERT ( mpOpenProgDlg );

		mpDatabaseItemsCtrl->SortCollection();
		mpDatabaseItemsCtrl->Expand ( mpDatabaseItemsCtrl->GetRootItem() );

		mpOpenProgDlg->Destroy();
	}
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnBrowseBySelected ( wxCommandEvent & )
{
	const BrowseByClientData *bbcd = dynamic_cast < const BrowseByClientData * > 
		( mpBrowseByCtrl->GetClientObject ( mpBrowseByCtrl->GetSelection() ) );
	wxASSERT ( bbcd );

	if ( bbcd->GetBrowseBy() != mpDatabaseItemsCtrl->GetBrowseBy() )
	{
		mpDatabaseItemsCtrl->SetBrowseBy ( bbcd->GetBrowseBy() );
		try
		{
			wxWindowUpdateLocker lock_upd ( this );
			mpParentInterface->GetHandler()->QueryAllItems();
			mpDatabaseItemsCtrl->Expand ( mpDatabaseItemsCtrl->GetRootItem() );
			Refresh();
		}
		catch ( DatabaseError &rE )
		{
			if ( mpOpenProgDlg ) mpOpenProgDlg->Destroy();
			wxLogError ( rE.what() );
			wxLogStatus ( _("Database Querying Failed") );
		}
	}
}

void col::AudioDatabaseLayout::OnRecycleBinItemRightClick ( wxListEvent &rEvent )
{
	boost::scoped_ptr < wxMenu > menu ( new wxMenu );

	menu->Append ( new wxMenuItem ( menu.get(), ID_PopupMenu_RestoreRecBinItem, 
		_("Restore" ), _("Restore Selected Item") ) );

	menu->Append ( new wxMenuItem ( menu.get(), ID_PopupMenu_DeleteRecBinItem, 
		_("Remove" ), _("Permanently Remove the Selected Item From the Database") ) );
	
	mpRecycleBinCtrl->PopupMenu ( menu.get(), rEvent.GetPoint() );
}

void col::AudioDatabaseLayout::OnPopupMenuRemRecBinItemPerm ( wxCommandEvent & )
{
	const DatabaseItemBase *album = mRecycleBinCtrlItemData[mpRecycleBinCtrl->GetItemData 
		( mpRecycleBinCtrl->GetSelectedItem() )];
	wxASSERT ( album );
	mpParentInterface->GetHandler()->DeleteItem ( *album );
}

void col::AudioDatabaseLayout::OnPopupMenuRestoreRecBinItem ( wxCommandEvent & )
{
	const DatabaseItemBase *album = mRecycleBinCtrlItemData[mpRecycleBinCtrl->GetItemData 
		( mpRecycleBinCtrl->GetSelectedItem() )];
	wxASSERT ( album );
	
	dynamic_cast < AudioDatabaseInterface * > 
		( mpParentInterface )->GetHandler()->SetAlbumAsTrashed 
		( album->GetParentId(), album->GetId(), false );
}

void col::AudioDatabaseLayout::OnRecBinKeyDown ( wxListEvent &rEvent )
{
	if ( rEvent.GetKeyCode() == WXK_DELETE )
	{
		if ( mpRecycleBinCtrl->GetSelectedItem() != -1 )
		{
			wxCommandEvent dummy; 
			OnPopupMenuRemRecBinItemPerm ( dummy );
		}
	}
}

void col::AudioDatabaseLayout::OnTrackSelected ( wxListEvent & )
{
	mpDeleteTrackButton->Enable();
	mpEditTrackButton->Enable();
}

void col::AudioDatabaseLayout::OnTrackDeselected ( wxListEvent & )
{
	mpDeleteTrackButton->Disable();
	mpEditTrackButton->Disable();
}

void col::AudioDatabaseLayout::OnTrackActivated ( wxListEvent & )
{
	wxCommandEvent dummy;
	OnButtonEditTrack ( dummy );
}

void col::AudioDatabaseLayout::OnItemRightClicked ( wxTreeEvent &rEvent )
{
	using namespace boost;

	if ( IsAlbumItem ( rEvent.GetItem() ) )
	{
		scoped_ptr < wxMenu > menu ( new wxMenu );

		typedef App::IconIndex ii;

		wxMenuItem *copy = new wxMenuItem ( menu.get(), wxID_COPY, _("Copy"), 
			_("Copy"), wxITEM_NORMAL );
		copy->SetBitmap ( wxGetApp().GetIcon ( ii::EditCopy ) );
		menu->Append ( copy );

		wxMenuItem *update = new wxMenuItem ( menu.get(), col::ID_Menu_UpdateItem, 
			_("Update"), _("Update"), wxITEM_NORMAL );
		update->SetBitmap ( wxGetApp().GetIcon ( ii::EditUpdate ) );
		menu->Append ( update );

		wxMenuItem *del = new wxMenuItem ( menu.get(), wxID_DELETE, _("Delete"), 
			_("Delete"), wxITEM_NORMAL );

		del->SetBitmap ( wxGetApp().GetIcon ( ii::EditRemove ) );

		menu->Append ( del );

		mpDatabaseItemsCtrl->PopupMenu ( menu.get(), rEvent.GetPoint() );
	}
}

void col::AudioDatabaseLayout::OnImport ( DataImporterEvent &rEvt )
{
	static wxUint32 cur_item = 0;

	if ( rEvt.GetEventType() == COLLIGERE_EVT_IMPORT_START )
	{
		mpImportProgDlg = new wxProgressDialog ( _("Importing..."), 
			_("Importing, please wait..."), rEvt.GetImporter()->GetItemCount(),
			this, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME );
		mpImportProgDlg->SetFocus();
	}
	else if ( rEvt.GetEventType() == COLLIGERE_EVT_ITEM_IMPORTED )
	{
		wxASSERT ( mpImportProgDlg );
		mpImportProgDlg->Update ( ++cur_item );
	}
	else if ( rEvt.GetEventType() == COLLIGERE_EVT_IMPORT_END )
	{
		wxASSERT ( mpImportProgDlg );
		mpImportProgDlg->Destroy();
		mpDatabaseItemsCtrl->SortCollection();
	}
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnExport ( DataExporterEvent &rEvt )
{
	static wxUint32 cur_item = 0;

	if ( rEvt.GetEventType() == COLLIGERE_EVT_EXPORT_START )
	{
		cur_item = 0;

		mpExportProgDlg = new wxProgressDialog ( _("Exporting..."), 
			_("Exporting, please wait..."), rEvt.GetExporter()->GetItemCount(),
			this, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME );
		mpExportProgDlg->SetFocus();
	}
	else if ( rEvt.GetEventType() == COLLIGERE_EVT_ITEM_EXPORTED )
	{
		wxASSERT ( mpExportProgDlg );
		mpExportProgDlg->Update ( ++cur_item );
	}
	else if ( rEvt.GetEventType() == COLLIGERE_EVT_EXPORT_END )
	{
		wxASSERT ( mpExportProgDlg );
		mpExportProgDlg->Destroy();
	}
	rEvt.Skip();
}

void col::AudioDatabaseLayout::OnPropertyGridChanged ( wxPropertyGridEvent &rE )
{
	if ( !IsAlbumItem ( mpDatabaseItemsCtrl->GetSelection() ) )
	{
		const wxPGProperty *p = rE.GetProperty();
		const wxString artist ( p->GetGrid()->GetPropertyByName 
			( "Artist" )->GetValueAsString() );
		const wxString album ( p->GetGrid()->GetPropertyByName 
			( "Album" )->GetValueAsString() );
		wxDynamicCast ( GetParent(), MainFrame )->EnableAdding 
			( !( artist.empty() || album.empty() ) );
	}
}

void col::AudioDatabaseLayout::OnClose ( wxCloseEvent & )
{
	if ( GetThread() && GetThread()->IsRunning() ) GetThread()->Wait();
	Destroy();
}

void col::AudioDatabaseLayout::OnThreadCompleted ( wxCommandEvent & )
{
	mpReleaseInfoCtrl->GetProperty ( "Format" )->SetChoices ( mFormatChoices );
	mpReleaseInfoCtrl->GetProperty ( "Format" )->SetValue("");

	mpReleaseInfoCtrl->GetProperty ( "Condition" )->SetChoices ( mConditionChoices );
	mpReleaseInfoCtrl->GetProperty ( "Condition" )->SetValue("");

	mpReleaseInfoCtrl->GetProperty ( "Status" )->SetChoices ( mStatusChoices );
	mpReleaseInfoCtrl->GetProperty ( "Status" )->SetValue("");

	mpReleaseInfoCtrl->GetProperty ( "Rating" )->SetChoices ( mRatingChoices );
	mpReleaseInfoCtrl->GetProperty ( "Rating" )->SetValue("");

	mpReleaseInfoCtrl->GetProperty ( "Genre" )->SetChoices ( mGenreChoices );
	mpReleaseInfoCtrl->GetProperty ( "Genre" )->SetValue("");

	mpReleaseInfoCtrl->GetProperty ( "Country" )->SetChoices ( mCountryChoices );
	mpReleaseInfoCtrl->GetProperty ( "Country" )->SetValue("");
}

///////////////////////////////////////
// ArtistNodeCounter
///////////////////////////////////////

col::AudioDatabaseLayout::ArtistNodeCounter::ArtistNodeCounter 
( const wxTreeCtrl *pCtrl, wxUint32 &rResult ) : mrResult ( rResult ),
mpCtrl ( pCtrl )
{
}

void col::AudioDatabaseLayout::ArtistNodeCounter::operator()
( wxTreeItemId item ) 
{
	if ( item.IsOk() && ( dynamic_cast < ArtistTreeItemData * > 
		( mpCtrl->GetItemData ( item ) ) != 0  ) )
	{
		++mrResult;
	}
}

///////////////////////////////////////
// AlbumNodeCounter
///////////////////////////////////////

col::AudioDatabaseLayout::AlbumNodeCounter::AlbumNodeCounter 
( const wxTreeCtrl *pCtrl, wxUint32 &rResult ) : mrResult ( rResult ), 
mpCtrl ( pCtrl )
{
}

void col::AudioDatabaseLayout::AlbumNodeCounter::operator()
( wxTreeItemId item ) 
{
	if ( item.IsOk() && ( dynamic_cast < AlbumTreeItemData * > 
		( mpCtrl->GetItemData ( item ) ) != 0  ) )
	{
		++mrResult;
	}
}